
#include "ckcp.h"
#include "easylogging.h"

CKcp::CKcp()
{
	LOG(DEBUG) << "CKcp::CKcp";
	print_infO();
};
CKcp::~CKcp()
{
	uninitialize();
	LOG(DEBUG) << "CKcp::~CKcp";
};
CKcp::CKcp(const CKcp& a) 
{
	LOG(DEBUG) << "CKcp::(const CKcp& a)";
	print_infO();
	if(a.m_run)
	{
		LOG(ERROR)<<"error";
	}
	/*assert(a.m_run==false);*/
};


bool CKcp::initialize(uint32_t devno, void* user, int mode, 
			int (*cb_output)(const char *buf, int len, ikcpcb *kcp, void *user),
			void (*cb_writelog)(const char *log, struct IKCPCB *kcp, void *user, int level))
{
	m_devno = devno;
	m_mode = mode;
	m_user = user;
	m_output = cb_output;
	m_writelog = cb_writelog;

	std::lock_guard<std::mutex> lg(m_mutex);
	if (m_hkcp) return true;
	LOG(INFO) << "ikcp object create! conv=" << devno << ", mode=" << mode;

	reset();

	m_run = true;
	m_hthread = new (std::nothrow) std::thread(&CKcp::update_thread, this);
	return true;
};

void CKcp::uninitialize()
{
	std::unique_lock<std::mutex> lg(m_mutex);

	if (NULL == m_hkcp)	return;
	LOG(INFO) << "ikcp object release! conv=" << m_hkcp->conv 
				<< ", snd_una=" << m_hkcp->snd_una << ", snd_nxt=" <<m_hkcp->snd_nxt << ", rcv_nxt=" << m_hkcp->rcv_nxt;

	lg.unlock();
	int cnt = 0;
	while(!iqueue_is_empty(&m_hkcp->snd_queue) && (cnt++ < 100))
	{
		LOG(TRACE) << "ikcp has some data not send, wait 10 msec...";
		std::this_thread::sleep_for(std::chrono::milliseconds(10));
	}
	LOG(TRACE) << "ikcp send queue is empty";
	lg.lock();

	m_run = false;
	m_hthread->join();
	delete m_hthread;
	m_hthread = NULL;

	ikcp_release(m_hkcp);
	m_hkcp = NULL;
};

int CKcp::send(const char *buffer, int len)
{
	if (NULL == m_hkcp)
	{
		LOG(ERROR) << "ikcp object is NULL, m_devno=" << m_devno;
		return -1;
	}
	std::lock_guard<std::mutex> lg(m_mutex);
	return ikcp_send(m_hkcp, buffer, len);
}
int CKcp::recv()
{
	if (NULL == m_hkcp)
	{
		LOG(ERROR) << "ikcp object is NULL, m_devno=" << m_devno;
		return -1;
	}
	std::lock_guard<std::mutex> lg(m_mutex);
	return ikcp_recv(m_hkcp, buffer, BUF_LEN);
};
int CKcp::input(const char *data, long size)
{
	if (NULL == m_hkcp)
	{
		LOG(ERROR) << "ikcp object is NULL, m_devno=" << m_devno;
		return -1;
	}
	std::lock_guard<std::mutex> lg(m_mutex);
	IKCP_HEADERE* pkcpH = (IKCP_HEADERE*)data;
	if (pkcpH->una ==0 && pkcpH->sn == 0 && pkcpH->cmd==81/*IKCP_CMD_PUSH*/)
	{
		// 客户端连接复位了，
		LOG(WARNING) << "receive client sn=0/unan=0 segment, reset kcp object!";
		reset();
	}
	return ikcp_input(m_hkcp, data, size);
}

void CKcp::update_thread()
{
	while(m_run)
	{
		{
			std::lock_guard<std::mutex> lg(m_mutex);
			if (m_hkcp)
			{
#ifdef KCP_DEMO_CLOCK
			ikcp_update(m_hkcp, iclock());
#else
			time_t ts = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
			ikcp_update(m_hkcp, ts);
#endif // KCP_DEMO_CLOCK
			}
		}
		std::this_thread::sleep_for(std::chrono::milliseconds(10));
	}
};

void CKcp::print_infO() const
{
	LOG(DEBUG) << "hkcp=" << m_hkcp << ", m_hthread=" << m_hthread << ", m_run=" << m_run;
}
void CKcp::reset()
{
	LOG(DEBUG) << "reset: devno=" << m_devno << ", mode=" << m_mode << ", user=" << m_user << ", output=" << m_output;
	if (m_hkcp)	
		ikcp_release(m_hkcp);
	
	m_hkcp = ikcp_create(m_devno, m_user);
	ikcp_setmtu(m_hkcp, 1000);
	ikcp_setoutput(m_hkcp, m_output);
	ikcp_wndsize(m_hkcp, 128, 128);
	if (m_mode == 0) {
		ikcp_nodelay(m_hkcp, 0, 10, 0, 0);
	}
	else if (m_mode == 1) {
			ikcp_nodelay(m_hkcp, 0, 10, 0, 1);
	}
	else {
		ikcp_nodelay(m_hkcp, 1, 10, 2, 1);
		m_hkcp->rx_minrto = 10;
		m_hkcp->fastresend = 1;
	}

	m_hkcp->logmask = 0x1FFFF;
	m_hkcp->writelog = m_writelog;
}

/*
eg:
	int mode = 0;
	CKcp& kcp  = m_mapBusiClient[devno].KcpObj;
	kcp.initialize(devno, this, mode, 
		[](const char *buf, int len, ikcpcb *kcp, void *user)	// for output
		{
			CACConnClient* pThis = (CACConnClient*)user;
			// 真正向网络中发送数据包
			pThis->_process_send_mgmt_trans_msg_kcp(kcp->conv, buf, len);
			return 0;
		},
		[](const char *log, struct IKCPCB *kcp, void *user, int level) -> void	// for log
		{
			switch(level)
			{
				case IKCP_LEVEL_ERROR: 		LOG(ERROR)   << "ikcplog: " << log;	break;
				case IKCP_LEVEL_WARNING: 	LOG(WARNING) << "ikcplog: " << log;	break;
				case IKCP_LEVEL_INFO: 		LOG(INFO)    << "ikcplog: " << log;	break;
				case IKCP_LEVEL_DEBUG: 		LOG(DEBUG)   << "ikcplog: " << log;	break;
				case IKCP_LEVEL_TRACE: 		LOG(TRACE)   << "ikcplog: " << log;	break;
			};				
		} 
	);
	
	// 收到底层数据包, 调用
	kcp.input(data, length);

	// 然后调用recv函数从kcp中获取有效报文
	while(1)
	{
		int hr = kcp.recv();
		if (hr <= 0)
		{
			break;
		}
		_push_to_queue(devno, kcp.buffer, hr);
	}

	// 要发送数据包，调用
	kcp.send(msg.data(), msg.length());

	// 然后output函数会被回调进行数据发送

	kcp.uninitialize();

*/